// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CaeControlCmdHandleThread.h"
#include "../cae_agent/CaeSocketAgent.h"
#include "../cae_common/CaeAppCtrlCmdUtils.h"
#include "../cae_common/CaeVirtualDeviceConfig.h"
#include "../cae_agent/CaeAgent.h"
#include "../cae_common/CaeMsgCode.h"
#include "../cae_common/CaeCommon.h"
#include "../cae_service/CaeParamStorage.h"
#include "../cae_service/CaeHttpsJob.h"
#include "../cae_common/CaeJsonUtil.h"
#include "../cae_service/CaeMediaConfig.h"

namespace {
const int DEFAULT_WIDTH = 720;
const int DEFAULT_HEIGHT = 1280;
const int DEFAULT_BITRATE = 3000000;
const int DEFAULT_FRAME_RATE = 30;
const int DEFAULT_ENCODE_TYPE = 0;
const char* DEFAULT_FRAME_TYPE = "h264";
}

CaeControlCmdHandleThread::CaeControlCmdHandleThread(CaeDataPipe* pipe)
{
    this->m_threadStatus = CAE_THREAD_STATUS_NEW;
    this->m_cmdData = pipe;
    this->m_aesInfo = nullptr;
}

CaeControlCmdHandleThread::~CaeControlCmdHandleThread()
{
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    Deinit();
    LOGI("CaeControlCmdHandleThread destroy ok.");
}

bool CaeControlCmdHandleThread::CommandFilter(int commandType)
{
    if (commandType >= COMMAND_TYPE::CMD_MAX) {
        LOGE("Invalid command.");
        return false;
    }

    int agentStatus = CaeAgent::GetInstance().GetStatus();
    if (agentStatus == CaeAgentStatus::CAE_AGENT_INIT && (commandType == CMD_RECONNECT || commandType == CMD_RESUME)) {
        ResponseInvalidCmd(CAE_INVALID_CMD, false);
        return false;
    }

    if (agentStatus == CaeAgentStatus::CAE_AGENT_WAIT_RECONNECT && (commandType == CMD_RESUME)) {
        ResponseInvalidCmd(CAE_INVALID_CMD);
        return false;
    }
    if (agentStatus == CaeAgentStatus::CAE_AGENT_PAUSE &&
        (commandType == CMD_START || commandType == CMD_RECONNECT || commandType == CMD_PAUSE)) {
        ResponseInvalidCmd(CAE_INVALID_CMD);
        return false;
    }
    if (agentStatus == CaeAgentStatus::CAE_AGENT_RUNING) {
        if (commandType == CMD_START || commandType == CMD_RECONNECT || commandType == CMD_RESUME) {
            ResponseInvalidCmd(CAE_INVALID_CMD);
            return false;
        }
    }
    return true;
}

void CaeControlCmdHandleThread::ResponseInvalidCmd(const int code, bool closeClient)
{
    if (closeClient) {
        CaeAgent::GetInstance().CloseClient();
        LOGI("Invalid Cmd, client closed.");
        return;
    }

    std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(code);
    LOGI("Invalid Cmd, send response to client.");
    if (!ResponseCmdToClient(params)) {
        LOGE("Failed to send response.");
    }
}

void CaeControlCmdHandleThread::HandleStartCmd(map<string, string> &parameters)
{
    CaeAgent &caeAgentInstance = CaeAgent::GetInstance();
    CaeParamStorage &paramStorage = CaeParamStorage::GetInstance();
    CaeCommon::PrintfCurrentTime("Start verify the command");
    if (parameters.empty()) {
        return;
    }
    LOGI("Receive start control cmd.");
    bool isCmdFromWaitReconnectState = caeAgentInstance.GetStatus() == CaeAgentStatus::CAE_AGENT_WAIT_RECONNECT;
    if (isCmdFromWaitReconnectState) {
        CaeSocketAgent::GetInstance().StopPauseTimer();
    }

    if (!CheckAndStoreParams(parameters)) {
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_VERIFY_PARAMETER_MISSING);
        if (!ResponseCmdToClient(params)) {
            LOGE("Failed to send the CAE_VERIFY_PARAMETER_MISSING parameter.");
            caeAgentInstance.CloseClient();
        }
        return;
    }

    int queryAesKeyRetParam = QueryAesKeyAndVerifyData(parameters);
    if (queryAesKeyRetParam != CAE_VERIFY_SUCCESS) {
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(queryAesKeyRetParam);
        if (!ResponseCmdToClient(params)) {
            LOGE("Failed to send the queryAesKeyRetParam.");
            caeAgentInstance.CloseClient();
        }
        return;
    }
    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_VERIFY_SUCCESS);
    CaeCommon::PrintfCurrentTime("End verify the command");

    if (parameters.find(KEY_MEDIA_CONFIG) == parameters.end()) {
        LOGW("KEY_MEDIA_CONFIG not found.");
    } else {
        ParseMediaConfig(parameters[KEY_MEDIA_CONFIG]);
    }

    caeAgentInstance.SetStatus(CaeAgentStatus::CAE_AGENT_RUNING);

    if (!isCmdFromWaitReconnectState) {
        if (!CaeSocketAgent::GetInstance().StartTrailTimer()) {
            caeAgentInstance.CloseClient();
            return;
        }
        if (!CaeSocketAgent::GetInstance().StartNotouchTimer()) {
            caeAgentInstance.CloseClient();
            return;
        }
    }

    if (CaeMediaConfig::GetInstance().IsNeedSetPhysicalResolution()) {
        int ret = CaeEngineControl::GetInstance().SetPhysicalResolution();
        if (ret != SUCCESS) {
            LOGE("Failed to set physical resolution.");
            std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
            if (!ResponseCmdToClient(params)) {
                caeAgentInstance.CloseClient();
            }
            return;
        }
    }

    Result ret = CaeEngineControl::GetInstance().OpenMediaStream();
    if (ret == H265_NOT_SUPPORT || ret == ERR_VIDEO_POOL_ELB_FAULT) {
        CaeEngineControl::GetInstance().CloseMediaStream();
        caeAgentInstance.SetStatus(CaeAgentStatus::CAE_AGENT_INIT);
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_H265_NOT_SUPPORTED);
        if (!ResponseCmdToClient(params)) {
            CaeAgent::GetInstance().CloseClient();
        }
        return;
    }
    if (ret != SUCCESS) {
        LOGE("Failed to open media stream.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
        if (!ResponseCmdToClient(params)) {
            caeAgentInstance.CloseClient();
        }
        return;
    }

    if (!CaeEngineControl::GetInstance().OpenVirtualDevices()) {
        CaeEngineControl::GetInstance().CloseVirtualDevices();
        LOGE("Failed to open virtual devices.");
    }

    if (CaeConfigManage::EnableRemoteIme()) {
        CaeImeControl::GetInstance().EnableRemoteIme();
    }

    char* rspJson = CaeMediaConfig::GetInstance().BuildResponseVideoJsonConfig();
    if (rspJson == nullptr) {
        LOGE("Failed to open media stream.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_ENGINE_START_FAILED);
        if (!ResponseCmdToClient(params)) {
            caeAgentInstance.CloseClient();
        }
        return;
    }

    std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_START_SUCCESS, rspJson);
    if (!ResponseCmdToClient(params)) {
        LOGE("Failed to send the CAE_START_SUCCESS params.");
        caeAgentInstance.CloseClient();
        return;
    }
    CaeCommon::PrintfCurrentTime("Send start param success.");
    CaeSocketAgent::GetInstance().SendVirtualDeviceStartRequest();

    if (CaeConfigManage::EnableEventNotify()) {
        if (!isCmdFromWaitReconnectState) {
            time_t startAppTimestamp = time(nullptr);
            paramStorage.SetStartTime(startAppTimestamp);

            int ret = caeAgentInstance.m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::NOTIFY_START);
            if (ret < 0) {
                LOGE("Failed to add HttpsJob.");
                caeAgentInstance.CloseClient();
                return;
            }
        }
    }
}

bool CaeControlCmdHandleThread::CheckAndStoreParams(map<string, string> &parameters)
{
    if (parameters.find(KEY_TICKET) == parameters.end()) {
        LOGE("KEY_TICKET not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetTicket(parameters[KEY_TICKET]);

    if (parameters.find(KEY_SESSION_ID) == parameters.end()) {
        LOGE("SESSION_ID not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetSessionId(parameters[KEY_SESSION_ID]);


    if (parameters.find(KEY_AUTH_TS) == parameters.end()) {
        LOGE("KEY_AUTH_TS not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetAuthTs(parameters[KEY_AUTH_TS]);


    if (parameters.find(KEY_VERIFY_DATA) == parameters.end()) {
        LOGE("KEY_VERIFY_DATA not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetVerifyData(parameters[KEY_VERIFY_DATA]);

    if (parameters.find(KEY_ENCRYPTED_DATA) == parameters.end()) {
        LOGE("KEY_ENCRYPTED_DATA not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetEncyptedData(parameters[KEY_ENCRYPTED_DATA]);


    if (parameters.find(KEY_AES_IV) == parameters.end()) {
        LOGE("KEY_AES_IV not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetAesIv(parameters[KEY_AES_IV]);

    if (parameters.find(KEY_SDK_VERSION) == parameters.end()) {
        LOGE("KEY_SDK_VERSION not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetSdkVersion(parameters[KEY_SDK_VERSION]);

    if (parameters.find(KEY_PROTOCOL_VERSION) == parameters.end()) {
        LOGE("KEY_PROTOCOL_VERSION not found.");
        return false;
    }
    CaeParamStorage::GetInstance().SetProtolcolVersion(parameters[KEY_PROTOCOL_VERSION]);

    if (parameters.find(KEY_CLIENT_TYPE) == parameters.end()) {
        LOGW("KEY_CLIENT_TYPE not found.");
    }

    if (parameters.find(KEY_MAX_DISCONNECT_DURATION) != parameters.end()) {
        CaeParamStorage::GetInstance().SetMaxDisconnectDuration(
            parameters[KEY_MAX_DISCONNECT_DURATION]);
        LOGI("KEY_MAX_DISCONNECT_DURATION is %s.", parameters[KEY_MAX_DISCONNECT_DURATION].c_str());
    } else {
        LOGW("KEY_MAX_DISCONNECT_DURATION not found.");
        std::string timeString = "0";
        CaeParamStorage::GetInstance().SetMaxDisconnectDuration(timeString);
    }

    return true;
}

int CaeControlCmdHandleThread::QueryAesKeyAndVerifyData(map<string, string> &parameters)
{
    // 32位16进制数，测试使用
    std::string aesKey = "11111111111111111111111111111111";
    CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_VERIFYING);
    if (CaeConfigManage::EnableSignCheck()) {
        CaeHttpsJob &caeHttpsJob = CaeHttpsJob::GetInstance();
        caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
        CaeAgent::GetInstance().m_caeHttpsJobThread->AddHttpsJob(HttpsJobType::QUERY_AES_KEY);
        int index = 0;
        if (!caeHttpsJob.ContainResponse(HttpsJobType::QUERY_AES_KEY)) {
            return CAE_VERIFY_DECRYPT_FAILED;
        }
        HttpsResponse &responseQueryAesKey = caeHttpsJob.GetResponse(HttpsJobType::QUERY_AES_KEY);
        do {
            if (responseQueryAesKey.size > 0) {
                LOGI("Receive queryAesKey response.");
                break;
            }
            usleep(50000);
            index++;
        } while (index < 200);
        if (responseQueryAesKey.size <= 0) {
            LOGE("Failed to get queryAesKey response, queryIndex is %d", index);
            return CAE_VERIFY_AES_KEY_QUERY_FAILED;
        }
        if (!caeHttpsJob.ParseHttpsResponse(responseQueryAesKey, HttpsJobType::QUERY_AES_KEY)) {
            caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
            return CAE_VERIFY_AES_KEY_QUERY_FAILED;
        }
        string responseStr;
        responseStr = string(responseQueryAesKey.buffer, responseQueryAesKey.size);
        cJSON *root = cJSON_Parse(responseStr.c_str());
        if (!root) {
            LOGE("JSON Error before: %s.", cJSON_GetErrorPtr());
            caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
            return CAE_VERIFY_AES_KEY_QUERY_FAILED;
        }
        aesKey = CaeJsonUtil::GetJSONString(root, "aes_key");
        cJSON_Delete(root);
        caeHttpsJob.InitResponseMem(HttpsJobType::QUERY_AES_KEY);
    }

    if (aesKey.empty()) {
        LOGE("AesKey is null.");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    CaeParamStorage::GetInstance().SetAesKey(aesKey);
    int ret = memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
    if (ret != EOK) {
        LOGE("Failed to memset.");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    m_aesInfo->keyType = KEY_AES_128;
    m_aesInfo->tagLen = 16;
    m_aesInfo->strKey = aesKey;
    m_aesInfo->strIv = CaeParamStorage::GetInstance().GetAesIv();

    if (!ValidAesInfo(m_aesInfo)) {
        memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
        LOGE("AesInfo invalid");
        return CAE_VERIFY_AES_KEY_INVALID;
    }
    string encryptedData = CaeParamStorage::GetInstance().GetEncyptedData();
    if (!ParseStartInfo(encryptedData, m_aesInfo)) {
        return CAE_VERIFY_DECRYPT_FAILED;
    }

    return CAE_VERIFY_SUCCESS;
}

bool CaeControlCmdHandleThread::ParseStartInfo(string &encryptData, AesInfo *aesInfo)
{
    string decryptData;
    if (!CaeAesGcmDecrypt(encryptData, aesInfo, decryptData)) {
        LOGE("Failed to decrypt CaeAesGcm.");
        return false;
    }

    cJSON *root = cJSON_Parse(decryptData.c_str());
    if (!root) {
        LOGE("Json error before: %s.", cJSON_GetErrorPtr());
        return false;
    }

    string ip = CaeJsonUtil::GetJSONString(root, KEY_IP.data());
    CaeParamStorage::GetInstance().SetIp(ip);

    string port = CaeJsonUtil::GetJSONString(root, KEY_PORT.data());
    CaeParamStorage::GetInstance().SetPort(port);

    string sessionId = CaeJsonUtil::GetJSONString(root, KEY_SESSION_ID.data());
    if (sessionId.empty()) {
        LOGE("SessionId is invalid!");
        cJSON_Delete(root);
        return false;
    }
    CaeParamStorage::GetInstance().SetSessionId(sessionId);

    string touchTimeout = CaeJsonUtil::GetJSONString(root, KEY_TOUCH_TIMEOUT.data());
    LOGD("Touch timeout is %s.", touchTimeout.c_str());
    if (touchTimeout.empty()) {
        touchTimeout = "0";
    }
    CaeParamStorage::GetInstance().SetTouchTimeout(touchTimeout);

    string backgroundTimeout = CaeJsonUtil::GetJSONString(root, KEY_BACKGROUND_TIMEOUT.data());
    CaeParamStorage::GetInstance().SetBackgroundTimeout(backgroundTimeout);

    string availablePlayTime = CaeJsonUtil::GetJSONString(root, KEY_AVAILABLE_PLAYTIME.data());
    CaeParamStorage::GetInstance().SetAvailablePlayTime(availablePlayTime);

    string tmpVerifyStr;
    string userId = CaeJsonUtil::GetJSONString(root, KEY_USER_ID.data());
    CaeParamStorage::GetInstance().SetUserId(userId);
    tmpVerifyStr = ip + port + sessionId + backgroundTimeout + availablePlayTime + userId;

    string encodedStr;
    if (!CaeSignUtil::CAESha256(tmpVerifyStr, encodedStr)) {
        LOGE("Failed to get sha256 of tmpVerifyStr.");
        cJSON_Delete(root);
        return false;
    }

    if (encodedStr != CaeParamStorage::GetInstance().GetVerifyData()) {
        LOGE("Failed to verify encodedStr.");
        cJSON_Delete(root);
        return false;
    }

    if (!availablePlayTime.length()) {
        LOGE("AvailablePlayTime:%s is invalid", availablePlayTime.c_str());
        cJSON_Delete(root);
        return false;
    }
    LOGI("ParseStartInfo Success.");
    cJSON_Delete(root);
    return true;
}

void CaeControlCmdHandleThread::HandleSetMediaConfigCmd(map<string, string> &parameters)
{
    if (parameters.empty()) {
        LOGW("Parameters is empty");
        return;
    }
    if (parameters.find(KEY_MEDIA_CONFIG) == parameters.end()) {
        LOGW("KEY_MEDIA_CONFIG not found.");
        return;
    }
    bool oldMuteFlag, newMuteFlag;
    if (parameters[KEY_MEDIA_CONFIG].empty()) {
        LOGW("Media config is empty");
        return;
    } else {
        oldMuteFlag = CaeMediaConfig::GetInstance().IsMute();
        ParseMediaConfig(parameters[KEY_MEDIA_CONFIG]);
        newMuteFlag = CaeMediaConfig::GetInstance().IsMute();
    }

    if (oldMuteFlag != newMuteFlag) {
        if (newMuteFlag) {
            CaeEngineControl::GetInstance().CloseAudio();
        } else {
            CaeEngineControl::GetInstance().OpenAudio();
        }
    }

    bool ret = false;
    Result setMediaConfigRet = CaeEngineControl::GetInstance().SetMediaConfig();
    if (setMediaConfigRet != Result::SUCCESS) {
        LOGE("Failed to set media config.");
        ret = CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SET_MEDIA_CONFIG_ERROR);
        if (!ret) {
            LOGE("Failed to send CAE_SET_MEDIA_CONFIG_ERROR message.");
        }
    } else {
        ret = CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_SET_MEDIA_CONFIG_SUCCESS);
        if (!ret) {
            LOGE("Send CAE_SET_MEDIA_CONFIG_SUCCESS message success.");
        }
    }
}

void CaeControlCmdHandleThread::HandleReconnectCmd(map<string, string> &parameters)
{
    if (CaeParamStorage::GetInstance().GetSessionId().empty()) {
        LOGE("SessionId is NULL.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_RECONNECT_PARAMETER_INVALID);
        if (!ResponseCmdToClient(params)) {
            LOGE("Failed to send CAE_RECONNECT_PARAMETER_INVALID param to client.");
        }
        CaeAgent::GetInstance().CloseClient();
        return;
    }
    string tempSessionId;
    if (parameters.find(KEY_SESSION_ID) == parameters.end()) {
        LOGE("SESSION_ID not found.");
        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_RECONNECT_PARAMETER_INVALID);
        if (!ResponseCmdToClient(params)) {
            LOGE("Failed to send CAE_RECONNECT_PARAMETER_INVALID param to client failed");
        }
        return;
    } else {
        tempSessionId = parameters[KEY_SESSION_ID];
    }
    if (tempSessionId == CaeParamStorage::GetInstance().GetSessionId()) {
        bool trailTimeOut = CaeClientNotifyTool::GetInstance().GetTrailTimeoutState();
        bool touchTimeOut = CaeClientNotifyTool::GetInstance().GetTouchTimeoutState();
        if (trailTimeOut || touchTimeOut) {
            if (trailTimeOut) {
                CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_TRAIL_PLAY_TIMEOUT);
            }
            if (!trailTimeOut && touchTimeOut) {
                CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_NOTOUCH_TIMEOUT);
            }
        } else {
            CaeClientNotifyTool::GetInstance().SendMsgToClient(CAE_RECONNECT_SUCCESS);
            CaeAgent::GetInstance().Reconnect();
        }
    } else {
        LOGE("SessionId is invalid.");

        std::map<string, string> params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_RECONNECT_PARAMETER_INVALID);
        if (!ResponseCmdToClient(params)) {
            LOGE("Failed to send startFailedResponse.");
            CaeAgent::GetInstance().CloseClient();
            return;
        }
    }
}

void CaeControlCmdHandleThread::HandleResumeCmd(map<string, string> &parameters)
{
    std::map<string, string> params;
    CaeAgent::GetInstance().Resume();
    params = CaeAppCtrlCmdUtils::GenerateResponseParams(CAE_SWITCH_FOREGROUND_SUCCESS);
    if (!ResponseCmdToClient(params)) {
        LOGE("HandleResumeCmd send CAE_SWITCH_FOREGROUND_SUCCESS failed.");
        CaeAgent::GetInstance().CloseClient();
    }
}

bool CaeControlCmdHandleThread::ResponseCmdToClient(map<string, string> params)
{
    string cmdStr = CaeAppCtrlCmdUtils::GenerateCommand(std::move(params));
    size_t strLen = cmdStr.size();
    uint8_t *responseBuff = nullptr;
    responseBuff = (uint8_t *)malloc(strLen + sizeof(STREAM_MSG_HEAD));
    if (responseBuff == nullptr) {
        LOGE("Failed to malloc control start app response.");
        return false;
    }
    if (!CaeSocketAgent::GetInstance().IsSocketValid()) {
        LOGE("CaeClientDataHandleThread socket is invalid.");
        free(responseBuff);
        responseBuff = nullptr;
        return false;
    }
    StreamMsgHead msgHead {};
    uint8_t msgType = CAE_MSG_CONTROL;
    msgHead.type = msgType;
    msgHead.SetPayloadSize((uint32_t) strLen);
    msgHead.magicWord = CAE_STREAM_DELIMITER_MAGICWORD;
    msgHead.checksum = (uint8_t)GET_CAE_CHECKSUM(msgType);
    if (memcpy_s(responseBuff, sizeof(STREAM_MSG_HEAD), &msgHead, sizeof(STREAM_MSG_HEAD)) != EOK) {
        LOGE("Failed to memcpy msg head.");
        free(responseBuff);
        responseBuff = nullptr;
        return false;
    }

    if (memcpy_s(responseBuff + sizeof(STREAM_MSG_HEAD), strLen, cmdStr.data(), strLen) != EOK) {
        LOGE("Failed to memcpy msg data .");
        free(responseBuff);
        responseBuff = nullptr;
        return false;
    }

    int bytes = 0;
    bytes = CaeSocketAgent::GetInstance().Send(responseBuff, static_cast<int>(strLen + sizeof(STREAM_MSG_HEAD)));
    free(responseBuff);
    responseBuff = nullptr;
    if (bytes <= 0) {
        LOGE("CAE send %s to client error.", cmdStr.c_str());
        return false;
    }
    return true;
}

char* CaeControlCmdHandleThread::ParseMediaConfig(std::string mediaConfig)
{
    LOGI("Begin to parse media config %s", mediaConfig.c_str());
    if (mediaConfig.empty()) {
        LOGD("Media config is empty");
        return "";
    }

    map<string, string> mediaConfigMap =
        CaeAppCtrlCmdUtils::ParseCommand(mediaConfig.c_str(), mediaConfig.size(), SUB_COMMAND_SEPARATOR);

    if (mediaConfigMap.find(KEY_FRAME_RATE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_FRAME_RATE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetFrameRate(mediaConfigMap[KEY_FRAME_RATE].c_str());
    }

    if (mediaConfigMap.find(KEY_BITRATE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_BITRATE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetBitrate(mediaConfigMap[KEY_BITRATE].c_str(), true);
    }

    if (mediaConfigMap.find(KEY_STREAM_WIDTH) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_STREAM_WIDTH not found.");
    } else {
        CaeMediaConfig::GetInstance().SetStreamWidth(mediaConfigMap[KEY_STREAM_WIDTH].c_str());
    }

    if (mediaConfigMap.find(KEY_STREAM_HEIGHT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_STREAM_HEIGHT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetStreamHeight(mediaConfigMap[KEY_STREAM_HEIGHT].c_str());
    }

    if (mediaConfigMap.find(KEY_ENCODE_TYPE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_ENCODE_TYPE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetEncodeType(mediaConfigMap[KEY_ENCODE_TYPE].c_str());
    }

    if (mediaConfigMap.find(KEY_FRAME_TYPE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_FRAME_TYPE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetVideoFrameType(mediaConfigMap[KEY_FRAME_TYPE].c_str());
    }

    if (mediaConfigMap.find(KEY_REMOTE_SCHEDULING_ELB_IP) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REMOTE_SCHEDULING_ELB_IP not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRemoteSchedulingElbIp(mediaConfigMap[KEY_REMOTE_SCHEDULING_ELB_IP].c_str());
    }

    if (mediaConfigMap.find(KEY_REMOTE_SCHEDULING_ELB_PORT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REMOTE_SCHEDULING_ELB_PORT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRemoteSchedulingElbPort(mediaConfigMap[KEY_REMOTE_SCHEDULING_ELB_PORT].c_str());
    }

    if (mediaConfigMap.find(KEY_REPEAT_FPS) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_REPEAT_FPS not found.");
    } else {
        CaeMediaConfig::GetInstance().SetRepeatFps(mediaConfigMap[KEY_REPEAT_FPS].c_str());
    }

    if (mediaConfigMap.find(KEY_MUTE) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_MUTE not found.");
    } else {
        CaeMediaConfig::GetInstance().SetMute(mediaConfigMap[KEY_MUTE].c_str());
    }

    if (mediaConfigMap.find(KEY_QUALITY) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_QUALITY not found.");
    } else {
        CaeMediaConfig::GetInstance().SetQuality(mediaConfigMap[KEY_QUALITY].c_str());
    }

    if (mediaConfigMap.find(KEY_PHYSICAL_WIDTH) == mediaConfigMap.end() ||
        mediaConfigMap.find(KEY_PHYSICAL_HEIGHT) == mediaConfigMap.end()) {
        LOGD("MediaConfig: KEY_PHYSICAL_WIDTH or KEY_PHYSICAL_HEIGHT not found.");
    } else {
        CaeMediaConfig::GetInstance().SetNeedSetPhysicalResolution(true);
        uint32_t physicalWidth = CaeConfigManage::GetHardwareWidth();
        uint32_t physicalHeight = CaeConfigManage::GetHardwareHeight();
        uint32_t width = CaeCommon::StrToUInt32(KEY_PHYSICAL_WIDTH.c_str(),
                                                mediaConfigMap[KEY_PHYSICAL_WIDTH].c_str(),
                                                physicalWidth);
        uint32_t height = CaeCommon::StrToUInt32(KEY_PHYSICAL_HEIGHT.c_str(),
                                                 mediaConfigMap[KEY_PHYSICAL_HEIGHT].c_str(),
                                                 physicalHeight);
        if (width != 0 && height != 0) {
            if (((float)physicalWidth / float(physicalHeight)) >= ((float)width / (float)height)) {
                physicalWidth = static_cast<uint32_t>(physicalHeight * width / height);
            } else {
                physicalHeight = static_cast<uint32_t>(physicalWidth * height / width);
            }
            CaeMediaConfig::GetInstance().SetPhysicalHeight(physicalHeight);
            CaeMediaConfig::GetInstance().SetPhysicalWidth(physicalWidth);
        } else {
            LOGE("Invalid physical size, width is [%d], height is [%d]", width, height);
        }
    }

    cJSON *mediaConfigJson = cJSON_CreateObject();
    for (auto it = mediaConfigMap.begin(); it != mediaConfigMap.end(); ++it) {
        const char* key = it->first.c_str();
        if (key == KEY_FRAME_TYPE || key == KEY_REMOTE_SCHEDULING_ELB_IP) {
            cJSON_AddItemToObject(mediaConfigJson, key, cJSON_CreateString(it->second.c_str()));
        } else if (key == KEY_MUTE || key == KEY_QUALITY) {
            continue;
        } else {
            cJSON_AddItemToObject(mediaConfigJson, key, cJSON_CreateNumber(
                    CaeCommon::StrToUInt32(key, it->second.c_str(), 0)
                    ));
        }
    }
    char *mediaConfigStr = cJSON_Print(mediaConfigJson);
    cJSON_Delete(mediaConfigJson);

    LOGI("End to parse media config");
    return mediaConfigStr;
}

bool CaeControlCmdHandleThread::Init()
{
    m_aesInfo = new (std::nothrow) AesInfo();
    if (m_aesInfo == nullptr) {
        LOGE("Param m_aesInfo is null.");
        return false;
    }
    int ret = memset_s(m_aesInfo, sizeof(AesInfo), 0, sizeof(AesInfo));
    if (ret != EOK) {
        LOGE("Failed to memset m_aesInfo.");
        return false;
    }
    return true;
}

void CaeControlCmdHandleThread::Deinit() noexcept
{
    if (this->m_aesInfo != nullptr) {
        delete this->m_aesInfo;
        this->m_aesInfo = nullptr;
    }
}

void CaeControlCmdHandleThread::HandleCommand(int commandType, map<string, string> parameters)
{
    switch (commandType) {
        case COMMAND_TYPE::CMD_START: {
            CaeCommon::PrintfCurrentTime("Command type is CMD_START");
            HandleStartCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_STOP: {
            LOGI("Command type is CMD_STOP");
            CaeAgent::GetInstance().CloseClient();
            CaeSocketAgent::GetInstance().StopTrailTimer();
            CaeSocketAgent::GetInstance().StopNotouchTimer();
            break;
        }
        case COMMAND_TYPE::CMD_SET_MEDIA_CONFIG: {
            LOGI("Command type is CMD_SET_MEDIA_CONFIG");
            HandleSetMediaConfigCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_RECONNECT: {
            LOGI("Command type is CMD_RECONNECT");
            HandleReconnectCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_PAUSE: {
            LOGI("Command type is CMD_PAUSE");
            CaeAgent::GetInstance().Pause();
            break;
        }
        case COMMAND_TYPE::CMD_RESUME: {
            LOGI("Command type is CMD_RESUME");
            HandleResumeCmd(parameters);
            break;
        }
        case COMMAND_TYPE::CMD_REQ_IFRAME: {
            LOGI("Command type is CMD_REQ_IFRAME");
            CaeEngineControl::GetInstance().RequestIframe();
            break;
        }
        default: {
            LOGE("Unknown cmd type.");
            break;
        }
    }
}

void CaeControlCmdHandleThread::Run()
{
    char threadName[] = "CtrlThread";
    prctl(PR_SET_NAME, threadName);
    LOGI("CaeControlCmdHandleThread RUN.");
    if (!Init()) {
        LOGE("Init failed");
        Deinit();
        return;
    }

    if (m_cmdData == nullptr) {
        LOGE("DataPipe is null");
        return;
    }
    while (m_threadStatus == CAE_THREAD_STATUS_RUNNING) {
        CaePipeData *pipeData = m_cmdData->GetPipeData();
        if (pipeData != nullptr && pipeData->GetLen() > 0) {
            const char *receivedMsgString = (const char *)(pipeData->GetBuffer());
            map<string, string> parameters = CaeAppCtrlCmdUtils::ParseCommand(receivedMsgString,
                                                                              pipeData->GetLen());
            delete pipeData;
            pipeData = nullptr;

            if (parameters.find(KEY_COMMAND) == parameters.end()) {
                LOGE("KEY_COMMAND not found.");
                continue;
            }
            int commandType = atoi((parameters[KEY_COMMAND]).c_str());
            LOGI("CommandType is %d", commandType);
            if (!CommandFilter(commandType)) {
                continue;
            }
            HandleCommand(commandType, parameters);
        } else {
            if (pipeData != nullptr) {
                delete pipeData;
                pipeData = nullptr;
            }
        }
        usleep(10);
    }
    Deinit();
    m_threadStatus = CAE_THREAD_STATUS_EXIT;
    LOGI("CaeControlCmdHandleThread exit.");
}